Component, Props, State

#React #React-Component #React-Props #React-State

이 포스트는 Next.js 의 React-Foundation 튜토리얼을 보고 작성한 것입니다.
(튜토리얼의 보충 노트로 생각해주세요.)
가장 알아야할 기본적인 사항만을 알려주는 튜토리얼이라고 생각이 됩니다.
React 공식 홈페이지의 tic-tae-toe 튜토리얼보다 먼저 보면 좋을 것 같습니다.


1. Component

[Components](https://nextjs.org/learn/react-foundations/building-ui-with-components
화면을 그리다보면(html, css, react, xml, compose, swift-UI 등등을 이용해서 UI 구조를 만드는 것) 컴포넌트에 대해서 자기도 모르게 개념이 생기는 것을 느낄 수 있다. UI를 그릴 때 컴포넌트화 해두지 않으면 나중에 돌이킬 수 없는 강을 바라만 보고 있게 될 수가 있다.
"저희는 100dp*50dp에 round는 2dp, color는 solid, main-primary를 기본 버튼 컴포넌트로 사용할 거예요."라던가, "약관 동의와 마케팅 동의는 같은 컴포넌트를 재사용 할 건데요, 그 컴포넌트는 텍스트뷰 컴포넌트와 체크박스 컴포넌트로 구성될 거예요."라고 회의중에 언급이 된다면, 반 자동적으로 'BasicButton', 'AgreementComponent' 컴포넌트를 머리속에서 이미 만들고 있게된다.
리액트도 이 점을 잘 알고있고, 이 개념을 매우 잘 구현해 놓았다.

1-1. 그래서 컴포넌트가 뭐야?

컴포넌트는 UI 블록이다.
HTML로 생각하면 <img/>, <p/>, <button/>같은 것들이다. 이것들을 또 합칠 수 있지 않나? <div/><div class="card-component"> <img/> <p></p> <button/> </div>처럼.

1-2. 컴포넌트는 JS야 JSX야?

JS 함수의 형태를 하고있으면서 리턴값은 JSX인 그것이다. ㅋㅋ...
Weave 되어있다고 생각하면 된다. 스프링의 aop weaving처럼 무언가를 무언가에 엮어준다. 같은 느낌으로 JS에 JSX를 엮어준다고 생각해보자.
그래서 컴포넌트 함수는 컴포넌트로서 사용될 때 함수호출 메서드가 아니라 JSX 구문으로 사용된다.
정리하면, 컴포넌트는 함수로 만들고, JSX로 사용한다.고 생각하자.
+ 컴포넌트를 함수로 호출하는 안티패턴을 안티패턴인줄 모르고 사용할 때가 있었다. 한치앞을 예측할 수 없게 되니 주의하자.

<script type="text/jsx">
  const app = document.getElementById("app")

  //No header()! Yes Header()!
  function Header() {
     return (<h1>Develop. Preview. Ship.</h1>)
   }
 
  const root = ReactDOM.createRoot(app);
  //root.render(Header); // No function!
  root.render(<Header/>); // Yes JSX!
</script>

1-3. Nesting Component?

1-1.에서 예시를 들어준 것처럼 card-component 클래스를 가진 div는 내부에 세가지의 컴포넌트를 감싸서 하나의 컴포넌트가 되었다. 이 div처럼 컴포넌트로 구성되어있는 컴포넌트를 nesting component라고 한다.


2. Props

Props
어허이... 프롭스로 인해서 JSX의 진가가 발휘되기도 하지만 단방향성이라는 그것 때문에 렌더링지옥과 드릴링지옥에 빠지게하는 그녀석이다.

2-1. Props가 뭐야?

컴포넌트가 사용하는 변수
컴포넌트를 만들다보면 뼈대만 만들어놓고 컴포넌트를 가져다 쓸 때 채워넣어야 하는 부분이 생긴다. 예를들어, BasicButton을 컴포넌트로 만들었는데, 모양만 공통일 뿐, '회원가입하러 가기' 라거나 '이벤트 참여하러 가기' 라거나 버튼 제목은 모두 다 다를 것 아닌가? 그러면 이것을 매개변수로 놔둬야겠지. HTML에도 attribute로 받을 수 있는게 있지 않나? class="", style="", src=""같은 것들. 이것들이 Props이다.

Props는 컴포넌트 함수에 첫번째 파라미터로 전달된다. 그 이름이 props던 params던 그건 우리 마음이고 얘는 그냥 컴포넌트 함수 첫번째 인자에 Props 객체를 넣어준다. (destructuring을 해서 사용하면 더 직관적으로 사용할 수 있다.)

2-2. 어떻게 써?

JSX에서는 attribute key에 props 변수명을 적고, attribute value에 변수에 넣을 값을 적으면 된다. 컴포넌트 함수에서는 attribute key와 value가 딕셔너리 형태의 객체로 사용한다.

function HomePage() {
  return (
    <div>
      <Header title="React" />
    </div>
  );
}

function Header(props) { 
  console.log(props); // { title: "React" } 
  return <h1>{props.title}</h1>;
}

2-3. Curly Braces, {}?

위의 예제에서 JSX 구문에 <h1>props.title</h1>으로 입력하면 어떻게 될까? React가 아니라 props.title이 찍히겠지. props는 JS 변수다. <h1></h1>은 JSX다. 이 간극을 어떻게 해결할까? JSX한테 얘 JS니까 치환해서 써. 라고 알려줘야겠지. 이것이 Curly Braces의 역할이다.

{}안에는 어떤 JS구문이 와도 되고, 그 JS안에서 다시 JSX를 쓰고 그 안에서 다시 JS를 쓴다면 {}를 또 써주면 된다. 아래의 예시처럼.

function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];
 
  return (
    <div>
      <Header title="Develop. Preview. Ship." />
      <ul>
        {names.map((name) => (
          <li>{name}</li>
        ))}
      </ul>
    </div>
  );
}

3. State

State
여기서 Hook이 시작되지만, State가 뭔지만 알아가자.

3-1. State가 뭐야?

State는 말 그대로 '상태'이다.
버튼을 클릭할 때마다 <p>{counting}</p>변수의 값을 변경해줘야한다. 어떻게 하지? props는 스스로를 바꿀 수 없는데? 위에서 렌더링이 일어나면 초기화되어버리고 마는데?
이런 문제를 해결해주는게 State이다. iOS를 해봤다면 @State, 안드로이드를 해봤다면 LiveData나 StateFlow, SharedFlow등이 생각날 것이다. 이것이 그것이다.

튜토리얼에서 주목할만한 문구가 하나 있는데

Note: Unlike props which are passed to components as the first function parameter, the state is initiated and stored within a component. You can pass the state information to children components as props, but the logic for updating the state should be kept within the component where state was initially created.

네... 그렇습니다. 콜백메서드를 받던지 리덕스를 쓰세요. setter 유출하지 마세요. 라고 한다.
이에 대해서는 추후에 얘기해보도록 하자.

3-2. 어떻게 사용해?

useState() 라는 훅을 사용한다. 이것은 getter와 setter를 반환해준다. 이 getter와 setter를 destructring해서 받아놓고 read하고 싶을때는 getter, 변경하고 싶을때는 setter를 쓰면 된다.
+ setter의 네이밍컨벤션은 set<getter명>이다.

function HomePage() {
  // ...
  const [likes, setLikes] = React.useState(0);
 
  function handleClick() {
    setLikes(likes + 1);
  }
 
  return (
    <div>
      {/* ... */}
      <button onClick={handleClick}>Likes ({likes})</button>
    </div>
  );
}